/*
 * os3calendar.js
 *
 * Copyright (C) 2006 - 2010 - OS3 srl - http://www.os3.it
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this software; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
// Since Internet Exploder has some bugs, we have to work
// around them (by simply removing some features from the
// calendar for Exploders users: switch to Firefox!)
var os3cal_is_exploder = ( document.all ? true : false );

var OS3CAL_LOCALE = 
			{
				months: Array ( 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ),
				days: Array ( 'Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa' ),
				today: 'Today'
			};

/*
	CALLBACKS
*/
function os3cal_cback_set_day ( id, day, month, year )
{
	var cal = _os3_cals [ id ];

	cal.set_date ( year, month +1, day );

	if ( cal.is_popup ) cal.hide ();
}

/*
	PUBLIC FUNCTIONS
*/
var _os3_cals = new Array ();

function os3cal_register ( id, cal )
{
	_os3_cals [ id ] = cal;
}

function os3cal_add_month ( id, delta )
{
	var cal = _os3_cals [ id ];

	cal.cur_date.setMonth ( cal.cur_date.getMonth () + delta );
	cal._set_vals ( false );
	cal.render ();
}

function os3cal_add_year ( id, delta )
{
	var cal = _os3_cals [ id ];

	cal.cur_date.setYear ( cal.cur_date.getFullYear () + delta );
	cal._set_vals ( false );
	cal.render ();
}

function os3cal_today ( id )
{
	var cal = _os3_cals [ id ];
	var d = new Date ();
	var y = d.getYear ();

	if ( y < 1900 ) y += 1900;

	if ( cal.set_date ( y, d.getMonth() + 1,  d.getDate (),  false ) == false ) cal.render ();
}

function os3cal_year_select ( id )
{
	os3cal_int_hide ( id, "year", "cur_year" );
}

function os3cal_year_changed ( input, id )
{
	var cal = _os3_cals [ id ];

	if ( cal.set_date ( input.value, cal.cur_month, cal.cur_day, true ) == false ) cal.render ();
}

function os3cal_month_select ( id )
{
	var cal = _os3_cals [ id ];

	if ( ! cal.cbacks [ 'month-clicked' ] )
		os3cal_int_hide ( id, "month", "cur_month" );
	else
		cal.cbacks [ 'month-clicked' ] ( cal );
}

function os3cal_int_hide ( id, inp, val )
{
	var cal = _os3_cals [ id ];
	var div1 = document.getElementById ( id + "_" + inp + "_label" );
	var div2 = document.getElementById ( id + "_" + inp + "_select" );
	var input = document.getElementById ( id + "_" + inp + "_sel" );
	var s, t;

	div1.className += " hidden";
	div2.className = "month";

	input.value = cal [ val ] + ( inp == 'month' ? 1 : 0 );
	input.focus ();
}

function os3cal_month_changed ( input, id )
{
	var cal = _os3_cals [ id ];

	if ( cal.set_date ( cal.cur_year, input.value, cal.cur_day, true ) == false ) cal.render ();
}


/*  
	METHODS
*/

function os3cal_set_date ( year, month, day, quiet )
{
	year  = parseInt ( year, 10 );
	month = parseInt ( month, 10 ) -1;
	day   = parseInt ( day, 10 );

	if ( ( year <= 0 ) || ( month <= -1 ) || ( day <= 0 ) ) return false;
	if ( ( year == this.cur_year ) && ( month == this.cur_month ) && ( day == this.cur_day ) && ( ! this.fire_same_event ) ) 
	{
		if ( this.cbacks [ 'onclick' ] ) this.cbacks [ 'onclick' ] ( year, month, day, this );
		return false;
	}

	this.cur_date = new Date ( year, month, day );
	this._set_vals ( ! quiet );

	if ( ! quiet ) this.cbacks [ 'date_changed' ] ( year, month, day, this );

	return true;
}

function os3cal_get_header () 
{
	var s = '';
	var t;

	s += '<table border="0" cellpadding="0" cellspacing="0">';
	s += '<tr><td style="padding-right: 3px;">';
	s += '<div id="' + this.id + '_month_label" class="month" onclick="os3cal_month_select(\'' + this.id + '\')">';
	s += OS3CAL_LOCALE [ 'months' ] [ this.cur_month ];
	s += '<\/div>';
	s += '<div id="' + this.id + '_month_select" class="month hidden">';

	s += '<select id="' + this.id + '_month_sel" class="month_select" ';
	s += 'onblur="os3cal_month_changed(this, \'' + this.id + '\')" ';
	if ( ! os3cal_is_exploder ) s += 'onchange="os3cal_month_changed( this, \'' + this.id + '\')" ';
	s += '>';

	for ( t = 0; t < 12; t ++ )
	{
		s += '<option value="' + ( t + 1 ) + '">' + this.month_label [ t ] + '</option>';
	}

	s += '<\/select><\/div>';
	s += '<\/td><td>';

	s += '<div id="' + this.id + '_year_label" class="month" onclick="os3cal_year_select(\'' + this.id + '\')">';
	s += this.cur_year + '<\/div>';
	s += '<div id="' + this.id + '_year_select" class="month hidden">';
	s += '<input id="' + this.id + '_year_sel" type="text" size="4" maxlength="4" class="year_select" ';
	s += 'onchange="os3cal_year_changed(this,\'' + this.id + '\')" ';
	s += '/><\/div>';
	s += '<\/td><\/tr>';
	s += '<\/table>';
	return s; 
}

function os3cal_to_string ()
{
	var first_date = new Date ( this.cur_year, this.cur_month, 1 );
	var first_day  = first_date.getDay() +1;
	var rows = 5;
	var s = new String.buffer ();

	first_day -= this.first_day_of_week;
	if ( first_day <= 0 ) first_day = 7 - first_day;

	os3cal_register ( this.id, this );

   	if ( ( ( this.days_in_month [ this.cur_month ] == 31 ) && ( first_day >= 6 ) ) ||
       	     ( ( this.days_in_month [ this.cur_month ] == 30 ) && ( first_day == 7 ) ) ) rows = 6;
   	else if ( ( this.days_in_month [ this.cur_month ] == 28) && ( first_day == 1)) rows = 4;

	s.add ( '<div align="center" id="os3cal_' + this.id + '" class="os3calendar">' );
	s.add ( this._render_header () );
	s.add ( this._render_days ( first_day, rows ) );
	s.add ( '</div>' );

	return s.toString ();
}

function os3cal_render ( objId )
{
	/*
	if ( ! objId ) objId = this.id;
	this.id = objId;
	*/
	if ( objId ) this.id = objId;
	
	if ( ! this.id ) return;

	var obj = document.getElementById ( this.id );
	if ( ! obj ) return;

	if ( this.is_popup && this.currently_shown == false ) 
		obj.style.display = 'none';
	else
		this.currently_shown = true;

	obj.innerHTML = this.toString ();
}

function os3cal_meth_show ( base_element )
{
	//console.debug ( "\n\n calendar id = %s \n\n ", this.id );
	if ( this.is_popup && ( this.currently_shown == false ) ) 
	{
		var e = document.getElementById ( this.id );

		if ( base_element )
		{
			var top, left, width, nleft, render_w, body_size;

			e.style.position = 'absolute';

			left = base_element.offsetLeft || liwe.dom.get_offset_left ( base_element );
			top  = base_element.offsetTop || liwe.dom.get_offset_top ( base_element );

			var w = liwe.dom.get_size ( base_element );
			if ( w [ 0 ] )
				
				left += w [ 0 ];
			/*	
				left  = liwe.dom.get_offset_left ( base_element );
				top   = liwe.dom.get_offset_top ( base_element );

				e.style.display = 'block';
				width = e.offsetWidth;
				e.style.display = 'none';

				left = left - ( width / 2 );
				if ( left < 5 ) left = 5;
			*/
			e.style.left = left + "px";
			e.style.top = top + "px";

			e.style.display = 'block';
		} else
			e.style.display = 'block';
	}

	this.currently_shown = true;
}

function os3cal_meth_hide ()
{
	if ( this.is_popup && this.currently_shown ) 
		document.getElementById ( this.id ).style.display = 'none';

	this.currently_shown = false;
}

function os3cal_meth_overflow ( base_element, cnt_id )
{
	var el = $( this.id );
	if ( ! el || el.style.position == 'absolute' ) return;
	
	var w, left, body_size, render_size;
	w = ( base_element ) ? liwe.dom.get_size ( base_element ) : [ 20, 20 ];
	body_size = liwe.dom.get_size ( cnt_id ); //liwe.dom.get_body_size ();
	render_size = liwe.dom.get_size ( el );
	left = liwe.dom.get_offset_left ( el );
	top  = liwe.dom.get_offset_top ( el );
	// check right side
	if ( ( left + render_size [ 0 ] ) > body_size [ 0 ] )
		left -=  ( w [ 0 ] * 2 ) + render_size [ 0 ];
	el.style.left = left + 'px';
	// check bottom
	if ( ( top + render_size [ 1 ] ) > body_size [ 1 ] )
		top -=  ( w [ 1 ] * 2 ) + render_size [ 1 ];
	el.style.top = top + 'px';
}

function os3cal_make_div_id ( day_counter )
{
	var tmp_id, tmp_date, div;
	tmp_date = String.format ( "%s/%s/%s", this.cur_year, ( this.cur_month + 1 ), day_counter );
	tmp_id = String.format ( "%s:%s" , this.id, Date.formatted ( "%Y-%m-%d", tmp_date ) ); 
	div = '<div id="' + tmp_id + '"></div>';
	return div;
}

function os3cal_int_render_days ( first_day, rows )
{
	var sunday_col = 8 - this.first_day_of_week;
	if ( sunday_col == 0 ) sunday_col = 1;

   	var day_counter = 1;
   	var loop_counter = 1;
	var j, i;
	var s = '';
	var style, cback;

	s = '<div align="center" class="days"><table border="0" cellpadding="0" cellspacing="0">';
	
	// Add days labels
	if ( this.show_days_labels )
	{
		s += '<tr>';
		for ( j = this.first_day_of_week; j < this.first_day_of_week + 7; j ++ )
		{
			var x = j % 7;
			s += '<td class="days_label">' + this.days_label [ x ] + '<\/td>';
		}
		s += '<\/tr>';
	}

	// alert ( 'day: ' + this.selected_day + ' - month: ' + this.selected_month + ' - year: ' + this.selected_year );

   	for ( j = 1; j <= rows; j++) 
	{
      		s += '<tr>';
      		for ( i = 1; i < 8; i++ ) 
		{
         		if ( ( loop_counter >= first_day ) && ( day_counter <= this.days_in_month [ this.cur_month ] ) ) 
			{
            			if ( ( day_counter == this.selected_day ) && ( this.selected_year == this.cur_year) && ( this.selected_month == this.cur_month)  ) style = 'today';
				else
					style = 'day' + ( i % 2 );
				if ( i == sunday_col )
					style += ' sunday';

				if ( this.cbacks [ 'style_day' ] ) style = this.cbacks [ 'style_day' ] ( day_counter, this.cur_month, this.cur_year, style, i );
				cback = 'javascript:os3cal_cback_set_day ( \'' + this.id + '\', ' + day_counter + ',' + this.cur_month + ',' + this.cur_year + ')';
               			s += '<td class="' + style + '"><a href="' + cback + '">' + day_counter + '<\/a>';
				if ( this.cbacks [ 'day_content' ] ) s+= this.cbacks [ 'day_content' ] ( day_counter, this );
				s += '<\/td>';
            			day_counter ++;    
         		}
			else
			{
				style = loop_counter < first_day ? "day-before" : "day-after";
				if ( loop_counter + 1 == first_day ) style += ' day-before-first';
				s += '<td class="' + style + '">&nbsp;<\/td>';
			}
			loop_counter ++;
         	}
		s += '<\/tr>';

         	loop_counter ++;
      	}
	
	return s + '<\/table><\/div>';
}

function os3cal_int_render_header ()
{
	var s = '';

	s  = '<div class="header">';
	s += '<table border="0" width="100%"><tr><td align="left" class="os3cal-link" >';
	if ( this.show_navi_years )
		s += '<a href="javascript:os3cal_add_year(\'' + this.id + '\', -1 );">&laquo;<\/a>&nbsp;&nbsp;';

	s += '<a href="javascript:os3cal_add_month(\'' + this.id + '\', -1 );">&laquo;<\/a><\/td>';
	s += '<td align="center">';
	s += this.get_header();
	if ( this.show_today )
	{
		s += '<\/td><td align="center">';
		s += '<a href="javascript:os3cal_today(\'' + this.id + '\');">' + OS3CAL_LOCALE [ 'today' ] + '<\/a>&nbsp;';
	}
	s += '<\/td><td align="right" class="os3cal-link">';
	s += '<a href="javascript:os3cal_add_month(\'' + this.id + '\',  1 );">&raquo;<\/a>';

	if ( this.show_navi_years )
		s += '&nbsp;&nbsp;<a href="javascript:os3cal_add_year(\'' + this.id + '\',  1 );">&raquo;<\/a>';

	s += '<\/td><\/tr><\/table>';
	s += '<\/div>';
	return s;
}

function int_calc_offset_left ( base_elem, v )
{
	if ( ! base_elem.offsetParent ) return  ( v );
	if ( ( ! base_elem.offsetParent.offsetParent ) && ( base_elem.parentNode.nodeName != 'BODY' ) ) return ( v );
	return int_calc_offset_left ( base_elem.offsetParent, v + base_elem.offsetLeft );
}

function int_calc_offset_top ( base_elem, v )
{
	if ( ! base_elem.offsetParent ) return  ( v );
	if ( ( ! base_elem.offsetParent.offsetParent ) && ( base_elem.parentNode.nodeName != 'BODY' ) ) return ( v );
	return int_calc_offset_top ( base_elem.offsetParent, v + base_elem.offsetTop );
}

function os3cal_int_set_vals ( set_selected )
{
	var d, m, y;
	m = this.cur_date.getMonth ();
	y = this.cur_date.getYear ();
	d = this.cur_date.getDate ();

	if ( ( d == this.cur_day ) && ( m == this.cur_month ) && ( y == this.cur_year ) ) return;

	this.cur_month = m;
	this.cur_year = y;
	this.cur_day = d;

	if ( this.cur_year < 1000 ) this.cur_year += 1900;

   	if ( this.cur_month == 1) this.days_in_month [ 1 ] = ( ( this.cur_year % 400 == 0) || (( this.cur_year % 4 == 0 ) && ( this.cur_year % 100 !=0))) ? 29 : 28;

	if ( set_selected == true )
	{
		// Selected date splitted.
		this.selected_day = this.cur_day;
		this.selected_month = this.cur_month;
		this.selected_year  = this.cur_year;
	}

	if ( this.cbacks [ 'date_shown' ] ) this.cbacks [ 'date_shown' ] ( this.cur_year, this.cur_month + 1, this.cur_day, this );
	if ( this.currently_shown ) this.render ();
}

function OS3Calendar ()
{
	this.id = null;

	this.days_in_month = new Array ( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
	this.month_label = OS3CAL_LOCALE [ 'months' ];
	this.days_label  = OS3CAL_LOCALE [ 'days' ];


	this.cbacks = {
		// This cb is called each time the user "clicks" on a date
		"date_changed" : null,

		// This cb is called for each day that is rendered and it is used to define the
		// style the day must have
		// the callback prototype must be as the following:
		// 	function cback ( day_of_month, month_num, year, original_style, day_of_week )
		//
		// 	if  day_of_week == 1 it is sunday
		// the function must return a valid style string name (eg. "selected")
		"style_day" : null,

		// This cb is called each time the user changes the month viewed on the calendar
		"date_shown" : null,

		// This cb is called every time a day is clicked
		"onclick" : null,

		// This cb is called every time the user clicks on the calendar's month label
		"month-clicked" : null
	};

	// =====================================================================================
	// PRIVATE STUFF
	// =====================================================================================
	this.currently_shown = false;	// Flag T/F. If T, the calendar is currently being shown


	// =====================================================================================
	// PUBLIC ATTRS
	// =====================================================================================
	this.show_today = true;		// Flag T/F. If T, a "Today" link appears on calendar to
					// set the current date as the selected one

	this.is_popup = false;		// Flag T/F. If T, this calendar is a popup
	this.show_days_labels = true;	// Flag T/F. If T, days names are shown
	this.first_day_of_week = 1;	// 0 -> Sunday
	this.fire_same_event = false;	// Flag T/F. If T, date change event is fired even if
					// the user selects the same date twice
					
	this.show_navi_years = false;	// Flag T/F. If T, two more buttons "<<" and ">>" will appear
					// to move between month of the previous or next year

	// Date shown inside the calendar
	this.cur_date = new Date();

	// =====================================================================================
	// PUBLIC METHODS
	// =====================================================================================
	this.get_header = os3cal_get_header;
	this.set_date   = os3cal_set_date;
	this.render     = os3cal_render;
	this.show	= os3cal_meth_show;
	this.hide	= os3cal_meth_hide;
	this.toString   = os3cal_to_string;
	this.overflow	= os3cal_meth_overflow;
	
	// =====================================================================================
	// PRIVATE METHODS - DO NOT USE IN PRODUCTION CODE!!
	// =====================================================================================
	this._render_days = os3cal_int_render_days;
	this._render_header = os3cal_int_render_header;
	this._set_vals  = os3cal_int_set_vals;

	// =====================================================================================
	// INIT PROCEDURE
	// =====================================================================================

	this._set_vals ( true );
}
